package org.pwwong.scalautils.event

import scala.collection.jcl.WeakHashMap
import scala.collection.mutable
import org.pwwong.scalautils.collection.WeakSet

object Event {
  def apply[T]:Event[T] = new EventImpl[T]
}

abstract class Event[+T]{
  def bind[U>:T](boundedEvent:Event[U]):Event[T]
  def unbind[U>:T](boundedEvent:Event[U]):Event[T]
  
  def apply(ref:AnyRef)(runMe:T=>Any):Event[T]
  def removeListener(key:AnyRef):Event[T]
    
  def fire[U>:T](eventData:U):Unit
}

class EventImpl[+T] extends Event[T]{
  protected[this] var boundedEvents = new WeakSet[Event[_>:T]]
  protected[this] var listsMap = new WeakHashMap[AnyRef, mutable.Set[T=>Any]]
  
  def bind[U>:T](boundedEvent:Event[U]):Event[T] = {
    boundedEvents+=this
    this
  }
  
  def unbind[U>:T](boundedEvent:Event[U]):Event[T] = {
    boundedEvents-=this
    this
  }
  
  def apply(ref:AnyRef)(runMe:T=>Any):Event[T] ={
    if(listsMap.contains(ref))
      listsMap(ref)+=runMe
    else
      listsMap+(ref -> mutable.Set(runMe))
    this
  }
  def removeListener(key:AnyRef):Event[T] = {
    listsMap-=key
    if(key.isInstanceOf[Event[_>:T]])
      boundedEvents-=key.asInstanceOf[Event[_>:T]]
    
    this
  }
    
  def fire[U>:T](eventData:U):Unit = {
    // Handle bounded events
    for( evlistener <- boundedEvents.elements )
      evlistener.fire(eventData.asInstanceOf[T])
    
    // Handle other listeners
    //TODO maybe thread event listener invocation
    for( entry <- listsMap;
         listener <- entry._2 )
        listener(eventData.asInstanceOf[T])
  } 
}
